Skip to main content

Activity: common recipes

We will work through common scenarios in a provided codebase:

  • Generating boilerplate code (e.g., CRUD operations).
  • Writing tests.
  • Automating repetitive tasks (e.g., batch renaming variables) and documentation.

Generating boilerplate code

In this section, we will quickly create a Web API for an e-commerce application. All we will provide are the names of the entities and hope that Copilot will generate the rest.

Suggested prompts:

  • Create a Web API for an e-commerce application with the following entities: Product, Category, Order, Customer, Payment. Suggest reasonable field names and types for each entity. Use Flask and SQLAlchemy.

Here's some possible outcome:

const express = require('express');
const sequelize = require('./models');
const { Product } = require('./models/relationships');

const app = express();
app.use(express.json());

// Basic CRUD routes for Product
app.get('/products', async (req, res) => {
try {
const products = await Product.findAll();
res.json(products);
} catch (error) {
res.status(500).json({ error: error.message });
}
});

app.post('/products', async (req, res) => {
try {
const product = await Product.create(req.body);
res.status(201).json(product);
} catch (error) {
res.status(400).json({ error: error.message });
}
});

const PORT = process.env.PORT || 3000;

async function startServer() {
try {
await sequelize.sync();
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
});
} catch (error) {
console.error('Unable to start server:', error);
}
}

startServer();

And you should also get instructions on how to set things up like:

  1. Install required packages

    npm init -y
    npm install express sequelize
  2. Create the required folder structure

    mkdir models
  3. Copy the code files into their respective locations

  4. Run the server:

    node server.js

Writing tests

A huge win with Copilot is that it can generate a lot of tests quickly. However, you should always review the tests and make sure they are correct. Copilot is not perfect and it can generate tests that are not correct or that do not cover all the edge cases.

Open the file where you store the server code and use the Chat and ask it to generate tests for the API. You can use the following prompt:

Generate tests for this file
note

The more context you provide, the better the results. You could specify the the type of test, the framework you want to use, etc.

// Mock the models
jest.mock('./models/relationships');
jest.mock('./models');

describe('Server', () => {
let app;

beforeEach(() => {
// Clear all mocks before each test
jest.clearAllMocks();

// Reset the express app
app = express();
app.use(express.json());

// Import fresh instance of routes
require('./server');
});

describe('GET /products', () => {
it('should return all products', async () => {
const mockProducts = [
{ id: 1, name: 'Product 1' },
{ id: 2, name: 'Product 2' }
];

Product.findAll.mockResolvedValue(mockProducts);

const response = await request(app)
.get('/products');

expect(response.status).toBe(200);
expect(response.body).toEqual(mockProducts);
expect(Product.findAll).toHaveBeenCalled();
});

it('should handle errors', async () => {
Product.findAll.mockRejectedValue(new Error('Database error'));

const response = await request(app)
.get('/products');

expect(response.status).toBe(500);
expect(response.body).toHaveProperty('error');
});
});

describe('POST /products', () => {
it('should create a new product', async () => {
const newProduct = { name: 'New Product' };
const createdProduct = { id: 1, ...newProduct };

Product.create.mockResolvedValue(createdProduct);

const response = await request(app)
.post('/products')
.send(newProduct);

expect(response.status).toBe(201);
expect(response.body).toEqual(createdProduct);
expect(Product.create).toHaveBeenCalledWith(newProduct);
});

it('should handle validation errors', async () => {
const invalidProduct = { name: '' }; // Assuming name is required

const response = await request(app)
.post('/products')
.send(invalidProduct);

expect(response.status).toBe(400);
expect(response.body).toHaveProperty('error', 'Validation error');
})

it('should handle database errors', async () => {
const newProduct = { name: 'New Product' };

Product.create.mockRejectedValue(new Error('Database error'));

const response = await request(app)
.post('/products')
.send(newProduct);

expect(response.status).toBe(500);
expect(response.body).toHaveProperty('error', 'Database error');
})
})
});

Automating repetitive tasks

There are some common tasks that you will need to do over and over again. For example, renaming variables, changing the structure of a file, etc. Your IDE can help with renaming variables, but Copilot can help with more complex tasks like adding new fields to a class, changing the structure of a file, etc. Let's try that:

note

Have think about the result, what would you change? You're dealing with ORMs, do we need a migration? Do we need to update the database? Do we need to update the API? As you can see, there might be a lot of things to do. Copilot can help with that, but you need to be careful and review the changes.

Open the file that contains a model, type the following prompt and let Copilot do the rest:

Add fields for create date and updated date and any restrictions you think are necessary. 

You should get something like this:

const { DataTypes } = require('sequelize');
const sequelize = require('./index');

const Product = sequelize.define('Product', {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
name: {
type: DataTypes.STRING(200),
allowNull: false
},
description: {
type: DataTypes.TEXT
},
price: {
type: DataTypes.DECIMAL(10, 2),
allowNull: false
},
stock: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: 0
},
createdAt: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: DataTypes.NOW
},
updatedAt: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: DataTypes.NOW
}
});

module.exports = Product;